//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2005
//
// 'Map properties' dialog, 'Header' tab.
//

# include "StdAfx.h"
# include "Resource.h"
# include "MapProperties.h"
# include "Map.h"
# include "MapView.h"
# include "Types.h"
# include "ChangeMapHeader.h"
# include "ChangeTypeSet.h"
# include "ParseHelpers.h"
# include "PropertyPages.h"
# include "Globals.h"

struct CodePageInfo_t {
	UINT uiCodePage;
	string_t strName;
};
typedef std::list<CodePageInfo_t> cps_t;
static cps_t g_cps;

typedef BOOL (WINAPI * pfnGetCPInfoEx_t) (UINT _CodePage, DWORD _dwFlags, LPCPINFOEX _lpCPInfoEx);
static pfnGetCPInfoEx_t g_pfnGetCPInfoEx;

static
BOOL CALLBACK _EnumCodePagesProc (LPTSTR _pCodePageString) {
	const UINT uiCodePage = ParseDecU (_pCodePageString);
	if (/*870*/0 > uiCodePage || uiCodePage > 1300)
		return TRUE;
	if (1000 <= uiCodePage && uiCodePage <= 1200)
		return TRUE;

	CodePageInfo_t data;
	data.uiCodePage = uiCodePage;

	if (g_pfnGetCPInfoEx) {
		CPINFOEX cpi;
		if (g_pfnGetCPInfoEx (uiCodePage, 0, & cpi))
			data.strName = cpi.CodePageName;
		else
			data.strName = _pCodePageString;
	} else
		data.strName = _pCodePageString;

	g_cps.push_back (data);

	return TRUE;
}

void InitCodePages () {
	g_cps.clear ();

	CodePageInfo_t data;
	data.strName = "0 (7-bit ASCII)";
	data.uiCodePage = 0;
	g_cps.push_back (data);

	const HMODULE hKernel32 = ::GetModuleHandle ("Kernel32.dll");
	g_pfnGetCPInfoEx = reinterpret_cast<pfnGetCPInfoEx_t> (::GetProcAddress (hKernel32, "GetCPInfoExA"));

	::EnumSystemCodePages (_EnumCodePagesProc, CP_SUPPORTED);
}

//////////////////////////////////////////////////////

INT_PTR CALLBACK _MapHeaderProc (HWND _hWnd, UINT _uMsg, WPARAM _wParam, LPARAM _lParam) {
	switch (_uMsg) {
		case WM_INITDIALOG: {
			CenterPropertyPages (_hWnd);

			//
			//
			//
			CCritSec::CLock lock (g_cs);

			// TypeSet.
			const HWND hTypeSet = ::GetDlgItem (_hWnd, IDC_MAP_TYPESET);
			::SetDlgItemText (_hWnd, IDC_MAP_TYPESET, g_map.pTypeSet->strDisplayName);
			size_t cOrdinal = 0;
			for (const CTypeSet * pTypeSet = CTypeSet::s_pFirst; pTypeSet; pTypeSet = pTypeSet->m_pNext) {
				if (pTypeSet != g_map.pTypeSet && g_map.pTypeSet->FindConvertTypeSetTo (pTypeSet) == NULL)
					continue;

				::SendMessage (hTypeSet, CB_INSERTSTRING, cOrdinal, (LPARAM) pTypeSet->strDisplayName);
				::SendMessage (hTypeSet, CB_SETITEMDATA,  cOrdinal, (LPARAM) pTypeSet);
				if (pTypeSet == g_map.pTypeSet)
					::SendMessage (hTypeSet, CB_SETCURSEL, cOrdinal, 0);
			}

			// ID.
			::SetDlgItemText (_hWnd, IDC_MAP_ID,        g_map.Header.strID.c_str ());

			// Name.
			::SetDlgItemText (_hWnd, IDC_MAP_NAME,      g_map.Header.strName.c_str ());

			// Copyright.
			char * const strCopyright = reinterpret_cast<char *> (alloca (sizeof (char)*(g_map.Header.strCopyright.size ()*2 + 1)));
			size_t cOut = 0;
			for (size_t c = 0; c < g_map.Header.strCopyright.size (); ++ c) {
				const char ch = g_map.Header.strCopyright [c];
				if (ch == '|') {
					strCopyright [cOut ++] = '\r';
					strCopyright [cOut ++] = '\n';
				} else
					strCopyright [cOut ++] = ch;
			}
			strCopyright [cOut ++] = '\0';
			::SetDlgItemText (_hWnd, IDC_MAP_COPYRIGHT, strCopyright);
			if ((g_map.pTypeSet->dwFeatures & CTypeSet::tsfHasCopyright) == 0)
				::EnableWindow (::GetDlgItem (_hWnd, IDC_MAP_COPYRIGHT), FALSE);

			// Datum.
			::SetDlgItemText (_hWnd, IDC_MAP_DATUM, "WGS 84");

			// Elevation.
			const HWND hElevation = ::GetDlgItem (_hWnd, IDC_MAP_ELEVATION);
			::SendMessage (hElevation, CB_INSERTSTRING, 0, (LPARAM) GetStr (IDS_UNITS_FEET));
			::SendMessage (hElevation, CB_SETITEMDATA,  0, unFeet);
			::SendMessage (hElevation, CB_INSERTSTRING, 1, (LPARAM) GetStr (IDS_UNITS_METERS));
			::SendMessage (hElevation, CB_SETITEMDATA,  1, unMeters);
			switch (g_map.Header.ElevationUnits) {
				case unFeet:   ::SendMessage (hElevation, CB_SETCURSEL, 0, 0); break;
				case unMeters: ::SendMessage (hElevation, CB_SETCURSEL, 1, 0); break;
				default: assert (false);
			}
			if ((g_map.pTypeSet->dwFeatures & CTypeSet::tsfHasElevationUnits) == 0)
				::EnableWindow (hElevation, FALSE);

			// Code page.
			const HWND hCodePage = ::GetDlgItem (_hWnd, IDC_MAP_CODEPAGE);
			InitCodePages ();
			cOrdinal = 0;
			for (cps_t::const_iterator i = g_cps.begin (); i != g_cps.end (); ++ i) {
				::SendMessage (hCodePage, CB_INSERTSTRING, cOrdinal, (LPARAM) i->strName.c_str ());
				::SendMessage (hCodePage, CB_SETITEMDATA,  cOrdinal, i->uiCodePage);
				if (i->uiCodePage == g_map.Header.wCodePage)
					::SendMessage (hCodePage, CB_SETCURSEL, cOrdinal, 0);
				++ cOrdinal;
			}

			// LblCoding.
			const HWND hLblCoding = ::GetDlgItem (_hWnd, IDC_MAP_LBLCODING);
			::SendMessage (hLblCoding, CB_INSERTSTRING, 0, (LPARAM) "American (7-bit)");
			::SendMessage (hLblCoding, CB_SETITEMDATA,  0, bpsAscii7);
			::SendMessage (hLblCoding, CB_INSERTSTRING, 1, (LPARAM) "European (single-byte)");
			::SendMessage (hLblCoding, CB_SETITEMDATA,  1, bpsSBCS);
			::SendMessage (hLblCoding, CB_INSERTSTRING, 2, (LPARAM) "Far East (multi-byte)");
			::SendMessage (hLblCoding, CB_SETITEMDATA,  2, bpsMBCS);
			switch (g_map.Header.CodeSchema) {
				case bpsAscii7: ::SendMessage (hLblCoding, CB_SETCURSEL, 0, 0); break;
				case bpsSBCS:   ::SendMessage (hLblCoding, CB_SETCURSEL, 1, 0); break;
				case bpsMBCS:   ::SendMessage (hLblCoding, CB_SETCURSEL, 2, 0); break;
				default: assert (false);
			}

			// File properties.
			::EnableWindow (::GetDlgItem (_hWnd, IDC_MAP_FILE_PROPERTIES), g_strFileName [0] != '\0');

			return TRUE;
		}

		case WM_COMMAND: {
			switch (HIWORD (_wParam)) {
				case CBN_SELCHANGE:
					switch (LOWORD (_wParam)) {
						case IDC_MAP_CODEPAGE: {
							const HWND hCodePage = ::GetDlgItem (_hWnd, IDC_MAP_CODEPAGE);
							const HWND hLblCoding = ::GetDlgItem (_hWnd, IDC_MAP_LBLCODING);

							const size_t cCodePageSel = ::SendMessage (hCodePage, CB_GETCURSEL, 0, 0);
							if (cCodePageSel != CB_ERR) {
								const WORD wCodePage = ::SendMessage (hCodePage, CB_GETITEMDATA, cCodePageSel, 0);

								// Update LblCoding to best value for the given CodePage.
								if (wCodePage == 0)
									::SendMessage (hLblCoding, CB_SETCURSEL, 0, 0);     // ASCII-7
								else {
									CPINFO cpi;
									::GetCPInfo (wCodePage, & cpi);
									if (cpi.MaxCharSize == 1)
										::SendMessage (hLblCoding, CB_SETCURSEL, 1, 0); // SBCS
									else
										::SendMessage (hLblCoding, CB_SETCURSEL, 2, 0); // MBCS
								}
							}

							PropSheet_Changed (::GetParent (_hWnd), _hWnd);
							break;
						}

						case IDC_MAP_TYPESET: {
							const HWND hTypeSet = ::GetDlgItem (_hWnd, IDC_MAP_TYPESET);

							const size_t cTypeSetSel = ::SendMessage (hTypeSet, CB_GETCURSEL, 0, 0);
							if (cTypeSetSel != CB_ERR) {
								const CTypeSet * const pTypeSet = reinterpret_cast<const CTypeSet *> (::SendMessage (hTypeSet, CB_GETITEMDATA, cTypeSetSel, 0));

								// TODO:
							}

							PropSheet_Changed (::GetParent (_hWnd), _hWnd);
							break;
						}

						case IDC_MAP_ELEVATION:
							PropSheet_Changed (::GetParent (_hWnd), _hWnd);
							break;
					}
					break;

				case EN_CHANGE:
					PropSheet_Changed (::GetParent (_hWnd), _hWnd);
					break;
			}

			switch (LOWORD (_wParam)) {
				case IDC_MAP_FILE_PROPERTIES:
					if (g_strFileName [0] != '\0') {
						SHELLEXECUTEINFO sei;
						::memset (& sei, 0, sizeof (sei));
						sei.cbSize = sizeof (sei);
						sei.fMask = SEE_MASK_INVOKEIDLIST;
						sei.hwnd = _hWnd;
						sei.lpVerb = "Properties";
						sei.lpFile = g_strFileName;
						sei.nShow = SW_SHOWNORMAL;
						::ShellExecuteEx (& sei);
					}
					break;
			}

			break;
		}

		case WM_NOTIFY: {
            const NMHDR * const pInfo = reinterpret_cast<NMHDR *> (_lParam);
            switch (pInfo->code) {
				case PSN_APPLY: {
					map_header_t Header = g_map.Header;

					// Type set.
					const HWND hTypeSet = ::GetDlgItem (_hWnd, IDC_MAP_TYPESET);
					const size_t cTypeSetSel = ::SendMessage (hTypeSet, CB_GETCURSEL, 0, 0);
					const CTypeSet * const pTypeSet = cTypeSetSel != CB_ERR ? reinterpret_cast<const CTypeSet *> (::SendMessage (hTypeSet, CB_GETITEMDATA, cTypeSetSel, 0)) : NULL;

					const size_t c_cBufSize = 512;
					char buf [c_cBufSize];

					// ID.
					::GetDlgItemText (_hWnd, IDC_MAP_ID,        buf, c_cBufSize);
					Header.strID = buf;

					// Name.
					::GetDlgItemText (_hWnd, IDC_MAP_NAME,      buf, c_cBufSize);
					Header.strName = buf;

					// Copyright.
					::GetDlgItemText (_hWnd, IDC_MAP_COPYRIGHT, buf, c_cBufSize);
					string_t strCopyright;
					for (size_t c = 0; c < c_cBufSize && buf [c] != '\0'; ++ c) {
						const char ch = buf [c];
						if (ch == '\r' && c + 1 < c_cBufSize && buf [c + 1] == '\n') {
							strCopyright.append (1, '|');
							++ c;
						} else
							strCopyright.append (1, ch);
					}
					Header.strCopyright.swap (strCopyright);

					// TODO:
					// Datum

					// Elevation.
					const HWND hElevation = ::GetDlgItem (_hWnd, IDC_MAP_ELEVATION);
					const size_t cElevationSel = ::SendMessage (hElevation, CB_GETCURSEL, 0, 0);
					if (cElevationSel != CB_ERR)
						Header.ElevationUnits = static_cast<elevation_units_t> (::SendMessage (hElevation, CB_GETITEMDATA, cElevationSel, 0));

					// CodePage.
					const HWND hCodePage = ::GetDlgItem (_hWnd, IDC_MAP_CODEPAGE);
					const size_t cCodePageSel = ::SendMessage (hCodePage, CB_GETCURSEL, 0, 0);
					if (cCodePageSel != CB_ERR)
						Header.wCodePage = ::SendMessage (hCodePage, CB_GETITEMDATA, cCodePageSel, 0);

					// LblCoding.
					const HWND hLblCoding = ::GetDlgItem (_hWnd, IDC_MAP_LBLCODING);
					const size_t cLblCodingSel = ::SendMessage (hLblCoding, CB_GETCURSEL, 0, 0);
					if (cLblCodingSel != CB_ERR)
						Header.CodeSchema = static_cast<CCodeSchema_t> (::SendMessage (hLblCoding, CB_GETITEMDATA, cLblCodingSel, 0));
					if (Header.wCodePage != 0 && Header.CodeSchema == bpsAscii7)
						Header.CodeSchema = bpsSBCS;

					bool bRefresh = false;
					if (! (Header == g_map.Header)) {
						if (Header.ElevationUnits != g_map.Header.ElevationUnits)
							bRefresh = true;

						ChangeMapHeader (Header);
					}

					if (pTypeSet && pTypeSet != g_map.pTypeSet) {
						ChangeTypeSet (pTypeSet, true);
						bRefresh = true;
					}

					if (bRefresh)
						Refresh ();

					break;
				}
			}
			break;
		}
	}

	return FALSE;
}
